﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using Common.Utilities;
using Microsoft.Office.Interop.Word;
using Font = Microsoft.Office.Interop.Word.Font;

namespace Workflows.Designer.OfficeUtil
{
    public class TableWriter
    {
        private readonly Application _App;
        private readonly Document _Doc;
        private Range _Range;
        private Table _Table;

        #region format

        private WdColor _HeaderBackColor = WdColor.wdColorGray25;
        /// <summary></summary>
        public WdColor HeaderBackColor
        {
            get
            {
                return this._HeaderBackColor;
            }
            set
            {
                this._HeaderBackColor = value;
            }
        }

        private WdColor _HeaderForeColor = WdColor.wdColorViolet;
        /// <summary></summary>
        public WdColor HeaderForeColor
        {
            get
            {
                return this._HeaderForeColor;
            }
            set
            {
                this._HeaderForeColor = value;
            }
        }
        private WdColor _BodyBackColor = WdColor.wdColorWhite;
        /// <summary></summary>
        public WdColor BodyBackColor
        {
            get
            {
                return this._BodyBackColor;
            }
            set
            {
                this._BodyBackColor = value;
            }
        }

        private WdColor _BodyForeColor = WdColor.wdColorBlack;
        /// <summary></summary>
        public WdColor BodyForeColor
        {
            get
            {
                return this._BodyForeColor;
            }
            set
            {
                this._BodyForeColor = value;
            }
        }
        private int _BorderWidth = 0;
        /// <summary></summary>
        public int BorderWidth
        {
            get
            {
                return this._BorderWidth;
            }
            set
            {
                this._BorderWidth = value;
            }
        }

        private Color _BorderColor = Color.Gray;
        /// <summary></summary>
        public Color BorderColor
        {
            get
            {
                return this._BorderColor;
            }
            set
            {
                this._BorderColor = value;
            }
        }

        private string _HeaderFontName = "Arial";
        /// <summary></summary>
        public string HeaderFontName
        {
            get
            {
                return this._HeaderFontName;
            }
            set
            {
                this._HeaderFontName = value;
            }
        }

        private string _BodyFontName = "Times New Roman";
        /// <summary></summary>
        public string BodyFontName
        {
            get
            {
                return this._BodyFontName;
            }
            set
            {
                this._BodyFontName = value;
            }
        }

        private int _HeaderFontSize = 12;
        /// <summary></summary>
        public int HeaderFontSize
        {
            get
            {
                return this._HeaderFontSize;
            }
            set
            {
                this._HeaderFontSize = value;
            }
        }

        private int _BodyFontSize = 10;
        /// <summary></summary>
        public int BodyFontSize
        {
            get
            {
                return this._BodyFontSize;
            }
            set
            {
                this._BodyFontSize = value;
            }
        }
        #endregion

        #region layout

        #endregion

        #region general

        public Application App
        {
            get { return _App; }
        }

        public Document Doc
        {
            get { return _Doc; }
        }

        public Range Range
        {
            get { return _Range; }
            set { this._Range = value; }
        }

        public Table Table
        {
            get { return _Table; }
        }

        #endregion

        #region ctor
        public TableWriter()
        {
            object oMissing = System.Reflection.Missing.Value;
            object oEndOfDoc = "\\endofdoc"; 
            this._App = new Microsoft.Office.Interop.Word.Application();
            this._App.Visible = true;
            this._Doc = this._App.Documents.Add(ref oMissing, ref oMissing,
                ref oMissing, ref oMissing);
            this._Range = this._Doc.Bookmarks.get_Item(ref oEndOfDoc).Range;
        }

        public TableWriter(Application app, Document doc)
        {
            this._App = app;
            this._Doc = doc;
            object oEndOfDoc = "\\endofdoc";
            this._Range = this._Doc.Bookmarks.get_Item(ref oEndOfDoc).Range;
        }

        public TableWriter(Application app, Document doc, Range range)
        {
            this._App = app;
            this._Doc = doc;
            this._Range = range;
        }
        #endregion

        #region add table
        public void InitTable(int rowCount, int colCount, List<string> colHeaders)
        {
            object oMissing = System.Reflection.Missing.Value;
            this._Table = this._Doc.Tables.Add(this._Range, rowCount + 1, colCount, ref oMissing, ref oMissing);
            for(int i=1;i<=colCount;i++)
            {
                this._Table.Cell(1, i).Range.Text = colHeaders[i];
            }
            this._Table.Rows[1].Range.Font.Size = this.HeaderFontSize;
            this._Table.Rows[1].Range.Font.Name = this.HeaderFontName;
            this._Table.Rows[1].Range.Font.Bold = 1;
            this._Table.Rows[1].Cells.Shading.BackgroundPatternColor = this.HeaderBackColor;
            this._Table.Rows[1].Cells.Shading.ForegroundPatternColor = this.HeaderForeColor;
            if (rowCount >= 1)
            {
                for (int i = 1; i <= rowCount; i++)
                {
                    this._Table.Rows[i + 1].Range.Font.Size = this.BodyFontSize;
                    this._Table.Rows[i + 1].Range.Font.Name = this.BodyFontName;
                    this._Table.Rows[i + 1].Range.Font.Bold = 0;
                    this._Table.Rows[i + 1].Cells.Shading.BackgroundPatternColor = this.BodyBackColor;
                    this._Table.Rows[i + 1].Cells.Shading.ForegroundPatternColor = this.BodyForeColor;
                }
            }
        }

        public void InsertTable(DataTable dtData)
        {
            List<string> colHeaders = new List<string>();
            foreach(DataColumn col in dtData.Columns)
            {
                colHeaders.Add(col.ColumnName);
            }
            this.InitTable(dtData.Rows.Count, dtData.Columns.Count, colHeaders);

            for(int i=0;i<dtData.Rows.Count;i++)
            {
                int rowIndex = i + 1 + 1;
                for(int k=0;k<dtData.Columns.Count;k++)
                {
                    int colIndex = k + 1;
                    this._Table.Cell(rowIndex, colIndex).Range.Text = (dtData.Rows[i][k] == null
                                                                           ? string.Empty
                                                                           : dtData.Rows[i][k].ToString());
                }
            }
        }

        public Row InsertRowAfter(int currentRowIndex)
        {
            object currentRow = null;
            if(currentRowIndex >0 && currentRowIndex<=this._Table.Rows.Count)
            {
                currentRow = this._Table.Rows[currentRowIndex];
            }
            else
            {
                currentRow = this._Table.Rows[this._Table.Rows.Count];
            }
            return this._Table.Rows.Add(ref currentRow);
        }

        public void SetCellData(int rowIndex, int colIndex, SimpleDataType dataType, object cellData)
        {
            SqlDbType dbType = FieldDataType.ToDbType(SimpleDataTypeUtil.ToSystemType(dataType).ToString());
            if(FieldDataType.IsBinaryType(dbType))
            {
                // insert image
                Image ruleNodeIcon = this.ResetTransparentColor(cellData as Image, Color.White);
                PictureWriter nodePicWriter =
                            new PictureWriter(ruleNodeIcon, string.Empty, string.Empty);
                string nodePicML = nodePicWriter.ToString();
                object missing = System.Reflection.Missing.Value;
                this._Table.Cell(rowIndex + 1, colIndex).Range.InsertXML(nodePicML, ref missing);
            }
            else
            {
                // string 
                this._Table.Cell(rowIndex + 1, colIndex).Range.Text = cellData.ToString();
            }
        }

        private Image ResetTransparentColor(Image img, Color color)
        {
            if (img.PixelFormat == PixelFormat.Format32bppArgb)
            {
                Bitmap bmp = (Bitmap)img.Clone();
                BitmapData bmpData =
                    bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite,
                                 PixelFormat.Format32bppArgb);
                int stride = bmpData.Stride;
                IntPtr scan0 = bmpData.Scan0;
                unsafe
                {
                    byte* p = (byte*)(void*)scan0;
                    int offset = stride - bmp.Width * 4;
                    // int width = bmp.Width*4;
                    for (int y = 0; y < bmp.Height; ++y)
                    {
                        for (int x = 0; x < bmp.Width; ++x)
                        {
                            int alpha = p[0];
                            if (alpha == 0)
                            {
                                p[0] = color.A;
                                p[1] = color.R;
                                p[2] = color.G;
                                p[3] = color.B;
                            }
                            p += 4;
                        }
                        p += offset;
                    }
                }
                bmp.UnlockBits(bmpData);
                return bmp;
            }
            else
                return img;
        }
        #endregion
    }
}
